#include "ros/ros.h"
#include "std_msgs/String.h"

#include "ServerThread.h"
#include "llm_server.h"

#include "conf.h"

#include <signal.h>

#include <ros/callback_queue.h>

#include "huzhou_common.h"

#include "llm_node/llm_srv.h"
#include "llm_node/llm_goBack.h"


namespace {

    enum requestType {
        SUB,
        SRV,
    };
    enum requestType request_type = SUB;


    ros::Publisher req_response_pub;

    ros::Publisher srv_response_pub;

    std::unique_ptr<LlmServer> lserver;


    std::string question;
    static ServerThread llmServerPostThread([]() {
        // sys_log_plain("lserver post: %s", res.c_str());
        lserver->post_request(question);
    });
    // sub callback : 有时候想要请求快些停止，不要阻塞
    void sub_question_callback(const std_msgs::String::ConstPtr& msg) {

        request_type = SUB;

        question = msg->data;

        sys_log_plain("receive question: %s", question.c_str());

        llmServerPostThread.notify();
    }


    std::string res;
    // srv callback : 有时候需要连续多次向大模型发送请求，这时候需要阻塞
    //                来保证上一次的访问完全结束，才能开启下一次访问
    bool srv_question_callback(llm_node::llm_srv::Request &req,
                            llm_node::llm_srv::Response &ret)
    {
        request_type = SRV;

        res = req.question;

        sys_log_plain("receive question: %s", res.c_str());

        lserver->deepseekPostRequest(res);

        return true;
    }


    void llmStreamingCallback(const std::string & res) {
        sys_log_plain("llm stream:[%s]", res.c_str());
        std_msgs::String msg;
        msg.data = res;

        if (request_type == SUB) {
            req_response_pub.publish(msg);

        } else if (request_type == SRV) {
            srv_response_pub.publish(msg);

        }

    }

    void llmCallback(const std::string & res) {
        sys_log_plain("llm server done.");
        std_msgs::String msg;
        msg.data = "[DONE]";

        if (request_type == SUB) {
            req_response_pub.publish(msg);

        } else if (request_type == SRV) {
            srv_response_pub.publish(msg);

        }
    }


    void signalHandler(int signum) {

        if (lserver)  lserver.reset();
    
        exit(signum);
    }
}


namespace _willing {
    bool willingServerCallback(llm_node::llm_goBack::Request & request, 
                               llm_node::llm_goBack::Response & response) {
        sys_log("willing server call.");

        std::string llmRet;

        response.willing = lserver->check_patient_ready_to_go_back(
            request.asrRes, llmRet);

        return true;
    }
}


int main(int argc, char ** argv) {

    signal(SIGINT, signalHandler);

    // 初始化 ros 节点
    ros::init(argc, argv, "llm_server");
    ros::NodeHandle nh;

    // ==================================================================================
    // 创建一个新的线程来处理"llm server"节点的回调，因为这个回调会阻塞线程   begin >>>>>>>>>>>>>>>>>>>>
    // ==================================================================================
    // 创建自定义队列
    ros::CallbackQueue custom_queue;

    // 将 NodeHandle 绑定到这个队列
    ros::NodeHandle nh_private;
    nh_private.setCallbackQueue(&custom_queue);

    // // 订阅 question -> 这个在整个过程中会阻塞
    // ros::Subscriber question_sub = nh_private.subscribe<std_msgs::String>(
    //     "huzhou_llm_req", 1, question_callback);

    // 不应该用publish ，应该用 service，因为需要进行阻塞
    ros::ServiceServer service = nh_private.advertiseService(
        "llm_server_srv", srv_question_callback);

    // 订阅 question -> 这个在整个过程中不会阻塞
    ros::Subscriber question_sub = nh_private.subscribe<std_msgs::String>(
        "huzhou_llm_req", 1, sub_question_callback);


    // 用独立线程处理这个队列
    std::thread queue_thread([&custom_queue]()
                             {
        while (ros::ok() && !hzc::exitFlag) {
            custom_queue.callAvailable(ros::WallDuration(0.1));  // 处理这个队列的回调
        }
        sys_log("exit 'custom queue thread'(people_id_lan service)."); });
    queue_thread.detach(); // 分离线程自己管自己的资源回收问题
    // ==================================================================================
    // 创建一个新的线程来处理"llm server"节点的回调，因为这个回调会阻塞线程   end <<<<<<<<<<<<<<<<<<<<<<
    // ==================================================================================

    req_response_pub = nh.advertise<std_msgs::String>(
        "huzhou_llm_res", 10);

    srv_response_pub = nh.advertise<std_msgs::String>(
        "llm_res_topic", 10);


    // 检查 patient 是否想要回到病房的 service 
    ros::ServiceServer asrServer = nh.advertiseService(
        "llm_willing_srv", _willing::willingServerCallback);






    lserver = std::make_unique<LlmServer>();

    lserver->setUserStreamCallback(llmStreamingCallback);

    lserver->setUserCallback(llmCallback);

    sys_log_plain("llm server init end.");

    ros::spin();
}

